#requires -Version 3

function Write-Log {
<#
.SYNOPSIS
Write to a log file
.DESCRIPTION
Writes a message to the log file and/or screen.
Supports https://support.microsoft.com/en-us/kb/316609
.PARAMETER Message
Message to send
.PARAMETER Severity
Messaging Stream
#>
    param(
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message,
        [switch] $LogTypeBasic,
        [ValidateRange(0,5)]
        [int]$Severity = 1
    )
    begin {
        $Tries = 0
        $Date = Get-Date
        $Component = Split-Path -Leaf -Path $PSCommandPath
    }
    process {

        foreach ( $Item in $Message ) {
            $Tries = 0
            do {
                try {
                    $LineOut = "[{0}]`t{1}" -f ($Date -f "s"),$Item
                    if ( -not $LogTypeBasic ) {
                        $LineOut = '<![LOG[{0}]LOG]!><time="{1}{2}" date="{3}" component="{4}" context="" type="{5}" thread="{6}" file="{4}">' -f $item,
                        $Date.ToString("hh:mm:ss.fff"),
                        [System.TimeZone]::CurrentTimeZone.GetUtcOffset([datetime]::Now).TotalMinutes,
                        $Date.ToString("MM-dd-yyyy"),
                        $Component,
                        $( switch($Severity){ 2 {3}; 3 {2}; 4 {2}; 5 {2}; default {1} } ),
                        $PID
                    }
                    $LineOut | add-content -path (Get-LogPath) -Encoding ascii
                    switch ( $Severity ) {
                        0 { $item | Out-Null }
                        2 { if ( $ErrorActionPreference -ne "Stop") { $Item | Out-String | Write-Error } }
                        3 { $Item | Out-String | Write-Warning }
                        4 { $Item | Out-String | Write-Verbose }
                        5 { $Item | Out-String | Write-Debug }
                        default { $Item | write-host }
                    }

                    $Tries = 0
                }
                catch [System.IO.IOException]{
                    $Tries++
                    if ( $Tries -gt 5 ) { throw "Write-Log Timeout error" }
                    start-sleep -Milliseconds 250
                }
            } while ( $Tries -gt 0 )
        }
    }
}

function Set-LogPath {
    param ( [Parameter(Mandatory=$true)] [string] $Path )
    $global:LogPath = $Path
}

function Get-LogPath {
    if ( -not $global:LogPath ) {
        $global:LogPath = "$env:temp\$(Split-Path -Leaf -Path $PSCommandPath).log"
        Write-Verbose -Message "LogPath set to $LogPath"
    }
    $global:LogPath
}

#region Wrappers

function Write-LogError {
    param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message
    )
    process { write-log @PSBoundParameters -Severity 2 }
}

function Write-LogWarning {
    param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message
    )
    process { write-log @PSBoundParameters -Severity 3 }
}

function Write-LogVerbose {
    param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message
    )
    process { write-log @PSBoundParameters -Severity 4 }
}

function Write-LogDebug {
    param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message
    )
    process { write-log @PSBoundParameters -Severity 5 }
}

function Write-LogHost {
    param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message
    )
    process { write-log @PSBoundParameters -Severity 1 }
}

function Write-LogOnly {
    param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$Message
    )
    process { write-log @PSBoundParameters -Severity 0 }
}

#endregion

#region TraceFunctions

function Trace-Begin {
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [System.Management.Automation.InvocationInfo] $CallerInvocation
    )
    Write-LogDebug -Message "Begin: $($CallerInvocation.MyCommand)"
    if ( $CallerInvocation.BoundParameters ) {
        Write-LogDebug -Message "Parameters: $($CallerInvocation.BoundParameters | Out-String)"
    }
    if ( $CallerInvocation.UnboundArguments ) {
        Write-LogDebug -Message "Unbound: $($CallerInvocation.UnboundArguments | Out-String)"
    }
}

function Trace-Process {
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [System.Management.Automation.InvocationInfo] $CallerInvocation
    )
    $CallerName = $CallerInvocation.InvocationName
    Write-LogDebug -Message  "Processing $CallerName"
}

function Trace-End {
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [System.Management.Automation.InvocationInfo] $CallerInvocation
    )
    $CallerName = $CallerInvocation.InvocationName
    Write-LogDebug -Message "Finished $CallerName"
}

#endregion

